package com.bright.cwduijie.duijie.service.impl;

import cn.hutool.core.util.CreditCodeUtil;
import com.bright.cwduijie.duijie.enums.DataApi;
import com.bright.cwduijie.duijie.enums.OpType;
import com.bright.cwduijie.duijie.pojo.command.DuijieCommand;
import com.bright.cwduijie.duijie.pojo.dto.CheckResultDto;
import com.bright.cwduijie.duijie.pojo.po.AccSet;
import com.bright.cwduijie.duijie.service.AbstractApiService;
import com.bright.cwduijie.duijie.util.ListUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Function;

/**
 * @Author hxj
 * @Date 2024/7/23 16:09
 * @Description
 */

@Component
@RequiredArgsConstructor
@Slf4j
public class CheckServiceImpl {

    private final Validator validator;

    public void doCheckBeforeStart(DuijieCommand duijieCommand, List<AccSet> needDuijieAccSets, List<AbstractApiService<?>> needDuijieApis) {
        log.info("开始检查账套数据。。。");

        // v1.5.10 过滤掉删除操作的账套
        List<AccSet> needCheckAccSets = ListUtil.toListByFilter(needDuijieAccSets,
                accSet -> accSet.getOpType() == null || accSet.getOpType() == OpType.ADD_OR_EDIT.getValue());
        StringBuilder stringBuilder = new StringBuilder();
        if (!duijieCommand.getNoCheck()) {
            // 校验账套
            CheckResultDto accSetDataValid = isAccSetDataValid(needCheckAccSets);
            if (!accSetDataValid.isSuccess()) {
                stringBuilder.append("社会信用代码校验失败：").append(accSetDataValid.getMessages());
            }
        }
        // 检验是否有重复data_key
        CheckResultDto checkDuplicate = checkDataKeyDuplicate(needDuijieApis);
        if (!checkDuplicate.isSuccess()) {
            stringBuilder.append("\n   DATA_KEY校验失败：").append(checkDuplicate.getMessages());
        }
        if (!stringBuilder.toString().isEmpty()) {
            stringBuilder.append("请先处理再推送！");
            log.info("校验异常：" + stringBuilder);
            throw new RuntimeException(stringBuilder.toString());
        }

    }

    /**
     * 检查是否有data_key相同的记录
     */
    public CheckResultDto checkDataKeyDuplicate(List<AbstractApiService<?>> needDuijieApis) {
        List<String> messages = new ArrayList<>();

        for (AbstractApiService<?> needDuijieApi : needDuijieApis) {
            CheckResultDto checkResultDto = needDuijieApi.checkDataKeyDuplicate();
            if (!checkResultDto.isSuccess()) {
                messages.addAll(checkResultDto.getMessages());
            }
        }

        boolean success = messages.size() == 0;

        return new CheckResultDto(success, messages);
    }

    /**
     * 检查账套表数据 主要是信用代码问题
     * @param accSets
     * @return
     */
    public CheckResultDto isAccSetDataValid(List<AccSet> accSets) {

        List<String> creditCodes = ListUtil.toSingleList(accSets, AccSet::getTYSHXYDM);

        // 检查是否存在重复统一社会信用代码
        List<String> repeatedCreditCodes = ListUtil.findRepeatedData(creditCodes);
        List<String> errorCreditCodes = new ArrayList<>();

        for (String creditCode : creditCodes) {
            //是否有不是18位的信用代码
            //是否有信用代码不合规的
            boolean isCreditCode = CreditCodeUtil.isCreditCode(creditCode);
            if (!isCreditCode) {
                errorCreditCodes.add(creditCode);
            }

        }

        List<String> message = new ArrayList<>();
        if (repeatedCreditCodes.size() > 0) {
            message.add("以下社会信用代码有重复，请检查：" + repeatedCreditCodes);
        }
        if (errorCreditCodes.size() > 0) {
            message.add("以下社会信用代码不合法，请检查：" + errorCreditCodes);
        }

        boolean success = message.size() == 0;
        return new CheckResultDto(success, message);
    }


    public <T> CheckResultDto checkDataCommon(List<T> dataList) {
        if (dataList.size() == 0) {
            return new CheckResultDto(true, null);
        }

        Class<?> aClass = dataList.get(0).getClass();
        List<String> messages = new ArrayList<>();
        for (T t : dataList) {
            Set<ConstraintViolation<T>> validate = validator.validate(t);
            if (validate.size() > 0) {
                for (ConstraintViolation<T> violation : validate) {
                    String message = violation.getPropertyPath() + "字段" + violation.getMessage() + ", 实际值为：";
                    try {
                        message += aClass.getDeclaredMethod("get" + violation.getPropertyPath()).invoke(t);
                    } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                    messages.add(message);
                }
            }
        }

        boolean success = messages.size() == 0;
        return new CheckResultDto(success, messages);
    }

    public <T> CheckResultDto checkSingleDataCommon(T data) {
        List<T> objects = data == null ? Collections.emptyList() : Collections.singletonList(data);
        return checkDataCommon(objects);
    }

    public <T> void checkDataBeforeSend(Map<String, CheckResultDto> errorMsgs, List<T> data, DataApi dataApi, Function<T, Integer> getIdFunc) {
        List<Integer> ids = ListUtil.toSingleList(data, getIdFunc);
        addErrorMsgs(errorMsgs, checkDataCommon(data), dataApi, ids);
    }

    public <T> void checkDataBeforeSend(Map<String, CheckResultDto> errorMsgs, Map<String, List<T>> map, DataApi dataApi, Function<T, Integer> getIdFunc) {
        List<T> data = new ArrayList<>();
        for (List<T> values : map.values()) {
            data.addAll(values);
        }
        List<Integer> ids = ListUtil.toSingleList(data, getIdFunc);
        addErrorMsgs(errorMsgs, checkDataCommon(data), dataApi, ids);
    }


    public <T> void addErrorMsgs(Map<String, CheckResultDto> errorMsgs, CheckResultDto checkSingleDataCommon, DataApi dataApi, List<Integer> ids) {
        if (!checkSingleDataCommon.isSuccess()) {
            String serviceCode = dataApi.getServiceCode();
            if (!errorMsgs.containsKey(serviceCode)) {
                List<String> emptyMsgList = new ArrayList<>();
                errorMsgs.put(serviceCode, new CheckResultDto(false, emptyMsgList));
            }
            CheckResultDto checkResultDto = errorMsgs.get(serviceCode);

            checkResultDto.setIds(ids);
            checkResultDto.getMessages().addAll(checkSingleDataCommon.getMessages());
        }
    }

}
